package com.nh.glory.ware.security.authentication.jwt;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nh.glory.data.mapper.RoleMapper;
import com.nh.glory.data.mapper.UserMapper;
import com.nh.glory.data.model.Role;
import com.nh.glory.data.model.User;
import com.nh.glory.ware.security.authentication.StatelessAuthenticationToken;
import com.nh.glory.ware.security.model.LoginUserDetails;
import com.nh.glory.ware.security.model.LoginUserDetailsBuilder;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {

    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final JwtFactory factory;

    public JwtAuthenticationProvider(UserMapper userMapper, RoleMapper roleMapper, JwtFactory factory) {
        this.userMapper = userMapper;
        this.roleMapper = roleMapper;
        this.factory = factory;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        JwtToken token = (JwtToken) authentication.getCredentials();
        String username = factory.getUsername(token);
        User user = getLoginUser(username);

        Role role = roleMapper.selectById(user.getRoleId());
        if (role == null) {
            throw new InsufficientAuthenticationException("User has no roles assigned");
        }

        List<String> roles = new ArrayList<>(1);
        roles.add(role.getRoleName());
        LoginUserDetails userDetails = LoginUserDetailsBuilder.creatOf(user, roles);
        return new StatelessAuthenticationToken(userDetails, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return StatelessAuthenticationToken.class.isAssignableFrom(authentication);
    }

    private User getLoginUser(String username) {
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getAccount, username));
        if (user == null) {
            throw new UsernameNotFoundException("user not found for " + username);
        }
        //todo add caches
        return user;
    }

}
